﻿using System.Collections.Generic;
using System.Linq;
using Engine;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace EngineDrawer.Debugging
{
    public class BoundingBoxDrawer : DrawableGameComponent
    {
        private List<DrawableBoundingBox> _drawableBoundingBoxes = new List<DrawableBoundingBox>();

        public BoundingBoxDrawer(Game game)
            : base(game)
        {
            DrawOrder = int.MaxValue;
        }

        #region Fields

        private BasicEffect _effect;
        private IndexBuffer _indexBuffer;

        #endregion

        #region CustomTypes

        private class DrawableBoundingBox
        {
            public VertexBuffer VertexBuffer { get; set; }
            public BoundingBox BoundingBox { get; set; }
        }

        #endregion

        #region Public Methods

        public IEnumerable<BoundingBox> GetAllBoundingBoxes()
        {
            return _drawableBoundingBoxes.Select(d => d.BoundingBox);
        }
        public void AddBoundingBox(BoundingBox boundingBox)
        {
            DrawableBoundingBox drawableBounding = new DrawableBoundingBox();
            _indexBuffer = CreateIndexBuffer();
            drawableBounding.BoundingBox = boundingBox;
            drawableBounding.VertexBuffer = CreateVertexBuffer(boundingBox);
            _drawableBoundingBoxes.Add(drawableBounding);
        }

        public void RemoveBoundingBox(BoundingBox boundingBox)
        {
            DrawableBoundingBox drawableBoundingToRemove;
            drawableBoundingToRemove = _drawableBoundingBoxes.Where(d => d.BoundingBox == boundingBox).FirstOrDefault();
            if (drawableBoundingToRemove != null)
                _drawableBoundingBoxes.Remove(drawableBoundingToRemove);
        }

        public void Clear()
        {
            _drawableBoundingBoxes.Clear();
        }

        #endregion

        #region Private Methods

        private IndexBuffer CreateIndexBuffer()
        {
            short[] inds = new short[] {0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7};
            var indexBuffer = new IndexBuffer(GraphicsDevice, typeof (short), inds.Length, BufferUsage.WriteOnly);
            indexBuffer.SetData(inds);
            return indexBuffer;
        }

        private VertexBuffer CreateVertexBuffer(BoundingBox boundingBox)
        {
            var vb = new VertexBuffer(GraphicsDevice, typeof (VertexPositionColor), 8, BufferUsage.WriteOnly);

            Vector3[] corners = boundingBox.GetCorners();
            VertexPositionColor[] verts = new VertexPositionColor[8];
            for (int i = 0; i < 8; i++)
                verts[i] = new VertexPositionColor(corners[i], Color);
            vb.SetData(verts);
            return vb;
        }

        #endregion

        #region Properties

        public Color Color { get; set; }

        public World World
        {
            get { return (World) Game.Services.GetService(typeof (World)); }
        }

        #endregion

        #region XNA overrides

        protected override void LoadContent()
        {
            _effect = new BasicEffect(GraphicsDevice);
            base.LoadContent();
        }

        public override void Initialize()
        {
            Color = Color.White;
            Game.Services.AddService(typeof (BoundingBoxDrawer), this);
            base.Initialize();
        }

        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;
            GraphicsDevice.Indices = _indexBuffer;
            foreach (DrawableBoundingBox drawableBoundingBox in _drawableBoundingBoxes)
            {
                _effect.World = Matrix.Identity;
                _effect.Projection = World.ProjMatrix;
                _effect.View = World.ViewMatrix;
                foreach (EffectPass pass in _effect.Techniques[0].Passes)
                {
                    pass.Apply();
                    GraphicsDevice.SetVertexBuffer(drawableBoundingBox.VertexBuffer);
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 8, 0, 12);

                }
            }
            base.Draw(gameTime);
        }

        #endregion

    }
}
